/*
 * $Id: PolisAcl.java 28 2007-04-13 22:31:27Z aaron.day $
 */
package org.crazydays.core.data;


import java.util.Set;
import java.util.HashSet;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;

import javax.persistence.*;

/**
 * Polis acl class.
 */
@Entity
@SequenceGenerator(name = "POLIS_IDS", sequenceName = "polis_ids", allocationSize = 1)
@Table(name = "polis_acl")
public class PolisAcl
	extends PolisData
{

	/** serial version uid */
	public final static long serialVersionUID = "$Id: PolisAcl.java 28 2007-04-13 22:31:27Z aaron.day $"
			.hashCode();

	/** acl: Admin */
	public final static String NAME_ADMIN = "Admin";

	/** acl: Guest */
	public final static String NAME_GUEST = "Guest";

	/** acl: User */
	public final static String NAME_USER = "User";

	/** user */
	@ManyToOne(cascade = { CascadeType.PERSIST, CascadeType.MERGE })
	@JoinColumn(name = "polis_user")
	protected PolisUser user;

	/** groups */
	// cascade = {CascadeType.PERSIST, CascadeType.MERGE},
	@ManyToMany(targetEntity = org.crazydays.core.data.PolisGroup.class, cascade = {
			CascadeType.MERGE, CascadeType.REFRESH }, fetch = FetchType.EAGER)
	@JoinTable(name = "polis_acl_group", joinColumns = { @JoinColumn(name = "polis_acl") }, inverseJoinColumns = { @JoinColumn(name = "polis_group") })
	protected Set<PolisGroup> groups = new HashSet<PolisGroup>();

	/** acl groups */
	@OneToMany(mappedBy = "key.aclId", fetch = FetchType.EAGER)
	@MapKey(name = "key")
	protected Map<PolisAclGroupKey, PolisAclGroup> aclGroups = new HashMap<PolisAclGroupKey, PolisAclGroup>();

	/**
	 * Set user.
	 * 
	 * @param user User
	 */
	public void setUser(PolisUser user)
	{
		this.user = user;
	}

	/**
	 * Get user.
	 * 
	 * @return User
	 */
	public PolisUser getUser()
	{
		return user;
	}

	/**
	 * Set groups.
	 * 
	 * @param groups Groups
	 */
	public void setGroups(Set<PolisGroup> groups)
	{
		this.groups = groups;
	}

	/**
	 * Get groups.
	 * 
	 * @return Groups
	 */
	public Set<PolisGroup> getGroups()
	{
		return groups;
	}

	/**
	 * Set acl groups.
	 * 
	 * @param aclGroups Acl groups map
	 */
	public void setAclGroups(Map<PolisAclGroupKey, PolisAclGroup> aclGroups)
	{
		this.aclGroups = aclGroups;
	}

	/**
	 * Get acl groups.
	 * 
	 * @return Acl groups map
	 */
	public Map<PolisAclGroupKey, PolisAclGroup> getAclGroups()
	{
		return aclGroups;
	}

	/**
	 * Add acl group to acl groups map.
	 * 
	 * @param aclGroup Acl group
	 */
	public void addAclGroup(PolisAclGroup aclGroup)
	{
		if (aclGroup == null) {
			throw new IllegalArgumentException("aclGroup == null");
		}
		if (aclGroup.getKey() == null) {
			throw new IllegalArgumentException(
					"aclGroup.key == null");
		}

		Map<PolisAclGroupKey, PolisAclGroup> aclGroups = getAclGroups();
		if (aclGroups == null) {
			throw new IllegalStateException("aclGroups == null");
		}

		aclGroups.put(aclGroup.getKey(), aclGroup);
	}

	/**
	 * Check allow.
	 * 
	 * @param group Group
	 * @param right Right
	 * @return Allow
	 */
	public boolean allow(PolisGroup group, PolisRight right)
	{
		if (group == null) {
			throw new IllegalArgumentException("group == null");
		}
		if (right == null) {
			throw new IllegalArgumentException("right == null");
		}

		return allow(group.getId(), right.getId());
	}

	/**
	 * Check allow.
	 * 
	 * @param groupId Group id
	 * @param rightId Right id
	 * @return Allow
	 */
	public boolean allow(long groupId, long rightId)
	{
		PolisAclGroupKey key = new PolisAclGroupKey();

		key.setAclId(getId());
		key.setGroupId(groupId);
		key.setRightId(rightId);

		PolisAclGroup aclGroup = getAclGroups().get(key);
		if (aclGroup == null) {
			return false;
		} else {
			return aclGroup.getAllow();
		}
	}

	/**
	 * Check allow for user.
	 * 
	 * @param user User
	 * @param right Right
	 * @return Allow
	 */
	public boolean allow(PolisUser user, PolisRight right)
	{
		if (user == null) {
			throw new IllegalArgumentException("user == null");
		}
		if (right == null) {
			throw new IllegalArgumentException("right == null");
		}

		boolean allow = false;

		// user's groups
		Set<PolisGroup> userGroups = user.getGroups();
		if (userGroups == null) {
			throw new IllegalStateException("userGroups == null");
		}

		// acl's groups
		Set<PolisGroup> aclGroups = getGroups();
		if (aclGroups == null) {
			throw new IllegalStateException("aclGroups == null");
		}

		// check the groups that intersect between the
		// user and this acl, then check group for allow
		Iterator<PolisGroup> i = userGroups.iterator();
		while (i.hasNext() && allow == false) {
			PolisGroup group = i.next();
			if (aclGroups.contains(group)) {
				allow = allow(group, right);
			}
		}

		return allow;
	}
}
